#ifndef _MATRIX_H_
#define _MATRIX_H_

#include<iostream>
#include<cstring>

using namespace std;
using std::memcpy;

template<typename T> class matrix;
template<typename T> class rowvector;
template<typename T> class colvector;

template<typename T> T operator * (const rowvector<T>&,const colvector<T>&);

template<typename T> class matrix{
private:
    int n_row;
    int n_col;
    T* matrix_elements;
public:
    matrix(){
        n_row=n_col=0;
        matrix_elements=nullptr;
    }
    ~matrix(){
        if(matrix_elements) delete matrix_elements;
    }
    matrix(int _row,int _col){
        if(_row<=0||_col<=0) cout<<"Error! Tring to input non-positive number as row(column) number!"<<endl;
        else{
            n_row=_row; n_col=_col;
            matrix_elements=new T [n_row*n_col];
            memset(matrix_elements,0,sizeof(T)*n_row*n_col);
        }
    }
    matrix(const matrix<T>& mpt){
        if(mpt.matrix_elements!=nullptr){
            n_row=mpt.n_row;
            n_col=mpt.n_col;
            matrix_elements=new T [n_row*n_col];
            memcpy(matrix_elements,mpt.matrix_elements,sizeof(T)*n_row*n_col);
        }
        else{
            n_row=n_col=0;
            matrix_elements=nullptr;
        }
    }
    bool is_matrix(){
        return (n_col==0||n_row==0)?false:true;
    }
    T& operator () (int _i,int _j){
        /*if(_i<1||_i>n_row||_j<1||_j>n_col){
            const T x=0;
            cout<<"Wrong index!"<<endl;
            return x;
        }*/
        return *(matrix_elements+(_i-1)*n_col+_j-1);
    }
    T& element(int _i,int _j){
        return *(matrix_elements+(_i-1)*n_col+_j-1);
    } 
    const T& use_me(int _i,int _j) const{
        return *(matrix_elements+(_i-1)*n_col+_j-1);
    }
    void mprint(){
        for(int i=0;i<n_row;i++){
            for(int j=0;j<n_col;j++) cout<<matrix_elements[i*n_col+j]<<' ';
            cout<<endl;
        }
    }
    bool is_square(){
        return n_row==n_col?true:false;
    }
    friend matrix<T> operator * (const matrix<T>& A,const matrix<T>& B){
        if(A.n_col!=B.n_row){
            cout<<"Error! The matrices can't be mupltiplied!"<<endl;
            matrix<T> c;
            return c;
        }
        matrix<T> c(A.n_row,B.n_col);
        for(int i=1;i<=c.n_row;i++){
            for(int j=1;j<=c.n_col;j++){
                for(int m=1;m<=A.n_col;m++)
                c(i,j)+=A.use_me(i,m)*B.use_me(m,j);
            }
        }
        return c;
    }
};

template<typename T> class rowvector:public matrix<T>{
private:
    int length;
public:
    rowvector():matrix<T>(){
        length=0;
    }
    rowvector(int _l):matrix<T>(1,_l){
        length=_l;
    }
    T& operator () (int _i){
        return matrix<T>::element(1,_i);
    }
    const int get_length() const{
        return length;
    }
    void printrow(){
        for(int i=1;i<=length;i++) cout<<matrix<T>::element(1,i) <<' ';
        cout<<endl;
    }
    friend T operator * (const rowvector<T>& A,const rowvector<T>& B){
        T c=0;
        if(A.length!=B.length){
            cout<<"Error! The row vectors can't be mupltiplied!"<<endl;
            return c;
        }
        for(int i=1;i<=A.length;i++)
        c+=A.use_me(1,i)*B.use_me(1,i);
        return c;
    }
    friend T operator * <> (const rowvector<T>&, const colvector<T>&);
   /* friend T operator * <T> (const rowvector<T> & A,const colvector<T>& B){
        T c=0;
        if(A.get_length()!=B.get_height()){
            cout<<"Error! The vectors can't be muptiplied!"<<endl;
            return c;
        }
        for(int i=0;i<A.length;i++)
        c+=A.use_me(1,i)*B.use_me(i,1);
        return c;
    }*/
};

template<typename T> class colvector:public matrix<T>{
private:
    int height;
public:
    colvector():matrix<T>(){
        height=0;
    }
    colvector(int _l):matrix<T>(_l,1){
        height=_l;
    }
    T& operator () (int _i){
        return matrix<T>::element(_i,1);
    }
    const int get_height()const{
        return height;
    }
    void printcol(){
        for(int i=1;i<=height;i++) cout<<matrix<T>::element(1,i)<<endl;
    }
    friend T operator * (const colvector<T>& A,const colvector<T>& B){
        T c=0;
        if(A.height!=B.height){
            cout<<"Error! The column vectors can't be mupltiplied!"<<endl;
            return c;
        }
        for(int i=1;i<=A.height;i++)
        c+=A.use_me(i,1)*B.use_me(i,1);
        return c;
    }
    friend T operator * <> (const rowvector<T>&, const colvector<T>&);
};

template<typename T> T operator * (const rowvector<T>& A,const colvector<T>& B){
    T c=0;
    if(A.get_length()!=B.get_height()){
        cout<<"Error! The vectors can't be muptiplied!"<<endl;
        return c;
    }
    for(int i=1;i<=A.length;i++)
    c+=A.use_me(1,i)*B.use_me(i,1);
    return c;
}
#endif